package jvm

import (
	"encoding/hex"
	"fmt"
	"hundsun.com/hsl/hschain/common/util/pb"
	"runtime"
	"sync"
	"time"

	"hundsun.com/hsl/hschain/common/config"
	"hundsun.com/hsl/hschain/common/log/logging"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/classfile"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/common"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/cpu"
	"hundsun.com/hsl/hschain/store/db"

	//"flag"
	//"fmt"
	"os"
	"runtime/pprof"

	//"strings"

	"github.com/allegro/bigcache"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/classpath"
	_ "hundsun.com/hsl/hschain/contract/jvm/jvm.go/native/all"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/rtda"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/rtda/heap"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/vm"
	"hundsun.com/hsl/hschain/contract/jvm/jvm.go/vmutils"
)

type RuntimePoolState int32

const (
	RunTimePoolStateIdle RuntimePoolState = 0
	RunTimePoolStateBusy RuntimePoolState = 1
)

type JVM struct {
	Opts          *vm.Options
	Rt            *heap.Runtime
	Cache         *bigcache.BigCache
	BootLoader    *heap.ClassLoader
	RunTime       *heap.Runtime
	PoolsMutex    sync.Mutex
	Logger        logging.Log
	ExecThreadNum int
}

func (j *JVM) Start(database db.Database) {
	jlog := j.Logger
	j.Opts = &vm.Options{}
	j.Opts.XUseJavaHome = true
	//base-contract.jar
	baseCtJarPath := config.GetStringConfigValue("chain.baseContractPath", "")
	if baseCtJarPath == "" {
		panic("chain.baseContractPath not set")
	}
	j.Opts.ClassPath = baseCtJarPath
	j.Opts.Init()
	//加载BaseContract
	cp := classpath.Parse(j.Opts)
	j.BootLoader = heap.NewBootloader(cp, database)
	j.BootLoader.Log = jlog
	st := time.Now().UnixNano()
	rt := heap.NewThreadRuntime(cp, false, j.BootLoader)
	rt.BootLoader().Cache = j.Cache
	j.Rt = rt
	et := time.Now().UnixNano()
	jlog.Infof("JVM started.................,create jvm runtime pools last %d ms\n", (et-st)/1e6)
}
func (j *JVM) CheckContract(code []byte) (bool, common.ChkCodeErrCode, string) {
	bl := j.BootLoader
	return bl.CheckContractClasses(code)
}

/*func (j*JVM)CallContract(code []byte)(bool,execCommon.ChkCodeErrCode) {
	j.Opts.MainClass = mainClass
	if j.Opts.XCPUProfile != "" {
		f, err := os.Create(j.Opts.XCPUProfile)
		if err != nil {
			panic(err)
		}
		_ = pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	mainThread := j.createMainThread(contractAddress,mainClass,args)
	cpu.Loop(mainThread)
	cpu.KeepAlive()
}*/
func traceMemStats(field string) {
	var ms runtime.MemStats
	runtime.ReadMemStats(&ms)
	fmt.Printf("[%s]Alloc:%d HeapAlloc:%d HeapSys:%d HeapObjects:%d HeapInuse:%d,HeapIdle:%d HeapReleased:%d\n", field, ms.Alloc,
		ms.HeapAlloc, ms.HeapSys, ms.HeapObjects, ms.HeapInuse, ms.HeapIdle, ms.HeapReleased)
}

func (j *JVM) StartJVM8(mainClass string, args []byte, log logging.Log, ctx *common.VMRunContext) {
	if j.Opts.XCPUProfile != "" {
		f, err := os.Create(j.Opts.XCPUProfile)
		if err != nil {
			panic(err)
		}
		_ = pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	startTime := time.Now().UnixNano() / 1e6

	rt := j.Rt
	rt.BootLoader().ContractAddress = ctx.ContractAddress
	rt.BootLoader().ExecCtx = ctx.ExecCtx
	//traceMemStats("Start thread")
	mainThread := j.createMainThread(mainClass, args, rt)
	//合约账户的内部地址为合约名称
	mainThread.ContractName = ctx.GroupedTx.Tx.ContractId.Name
	mainThread.ContractAddress = ctx.ContractAddress
	mainThread.GroupedTx = ctx.GroupedTx
	mainThread.ExecCtx = ctx.ExecCtx
	//交易支付的GasFee(应当扣除固定费部分)
	gasLimit := int64(pb.GetGenesisTransactionPayload().GetGasLimit())
	mainThread.GasLeft.Store(gasLimit)
	if ctx.DeployInit {
		mainThread.InitedFlag = true
	}
	jlog := log.WithField(logging.LogFieldTxHash, hex.EncodeToString(ctx.GroupedTx.TxExecRst.Receipt.TxHash)).
		WithField(logging.LogFieldBlockNumber, ctx.ExecCtx.GetHeight())

	mainThread.Log = jlog
	jlog.Infof("main thread %p start loop\n", mainThread)
	cpu.Loop(mainThread)
	//cpu.KeepAlive()
	mainThread.CondLock.Lock()
	if mainThread.AliveCount > 0 {
		mainThread.Cond.Wait()
	}
	mainThread.CondLock.Unlock()
	//合约执行完成，清除bootLoader的用户合约的classMap，基类和JDK相关的类不需要删除
	mainThread.Runtime.BootLoader().ClearContractClass()
	costFee := gasLimit - mainThread.GasLeft.Load()
	endTime := time.Now().UnixNano() / 1e6
	jlog.Infof("jvm executed over,last time %d ms,main class %s, cost fee %d", endTime-startTime, mainClass, costFee)
}

func (j *JVM) createMainThread(mainClass string, args []byte, rt *heap.Runtime) *rtda.Thread {
	mc := vmutils.DotToSlash(mainClass)
	bootArgs := []heap.Slot{heap.NewHackSlot(mc), heap.NewHackSlot(args)}
	mainThread := rtda.NewThread(nil, j.Opts, rt)
	mainThread.MainClass = mainClass
	shimClass := &heap.Class{Name: "~shim"}

	/*shimReturnMethod := &heap.Method{
		ClassMember: heap.ClassMember{
			AccessFlags: classfile.AccStatic,
			Name:        "<return>",
			Class:       shimClass,
		},
		MethodData: heap.MethodData{
			Code: []byte{0xb1}, // return
		},
	}

	shimAThrowMethod := &heap.Method{
		ClassMember: heap.ClassMember{
		AccessFlags: classfile.AccStatic,
		Name:        "<athrow>",
		Class:       shimClass,
	},
		MethodData: heap.MethodData{
			Code: []byte{0xbf}, // athrow
		},
	}*/

	shimBootstrapMethod := &heap.Method{
		ClassMember: heap.ClassMember{
			AccessFlags: classfile.AccStatic,
			Name:        "<bootstrap>",
			Class:       shimClass,
		},
		MethodData: heap.MethodData{
			Code:      []byte{0xff, 0xb1}, // bootstrap, return
			MaxStack:  8,
			MaxLocals: 8,
		},
		ParamSlotCount: 2,
	}

	mainThread.InvokeMethodWithShim(shimBootstrapMethod, bootArgs)
	return mainThread
}
